﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ReporterEngine.Methods;

namespace ReporterEngine
{
    public class Interpreter
    {
        private Interpreter()
        {
            if (Methods.Count == 0)
            {
                Methods.Add(new Types {Interpreter = this});
                Methods.Add(new Properties {Interpreter = this});
                Methods.Add(new Count {Interpreter = this});
                Methods.Add(new Distinct {Interpreter = this});
                Methods.Add(new UsedByReference {Interpreter = this});
                Methods.Add(new Condition {Interpreter = this});
                Methods.Add(new Members {Interpreter = this});
                Methods.Add(new VisibleMembers { Interpreter = this });

                Methods.Add(new Dimensions { Interpreter = this });

                Methods.Add(new IsMemberOf {Interpreter = this});

                Methods.Add(new IsDimensionOf { Interpreter = this });

                Methods.Add(new First {Interpreter = this});
                Methods.Add(new AllParents { Interpreter = this });
                Methods.Add(new AllChilds { Interpreter = this });
                Methods.Add(new ClosestParent { Interpreter = this });

                Methods.Add(new AscBy { Interpreter = this });
                Methods.Add(new DescBy { Interpreter = this });

                Methods.Add(new NoContextMembers { Interpreter = this });
                Methods.Add(new ContextMembers { Interpreter = this });
            }
        }

        //public methods list
        //method + toke as parameter + squery

        public static List<BaseMethod> Methods = new List<BaseMethod>();

        public bool StopOnEmptyData { get; set; }

        private LexPath _fullPath;

        public bool HasError;
        public LexPath.PathToken ErrorToken;
        public LexPath.PathToken EmptyToken;
        
        public static Interpreter Create(LexPath path)
        {
            return new Interpreter { _fullPath = path };
        }

        public static List<ModelMethod> GetAvailableMethods( sQuery query)
        {
            List<ModelMethod> result = new List<ModelMethod>();

            Methods.ForEach(x=>x.GetMethodNames()
                .Where(ua=>ua.IsUserMethod)
                .OrderBy(ua=>ua.Name)
                .ToList()
                .ForEach(y=> result.Add(new ModelMethod{Name = y.Name})));

            return result;
        }

        public void Validate()
        {
            
        }

        public sQuery Execute(ReporterEngine.LexPath.PathToken curToken, sQuery query)
        {
            var curQuery = query;
            
            while(curToken != null)
            {
                //execute cur token

                var methods = Methods.Where(x => x.IsUseable(curToken)).ToList();

                if (methods.Count ==0)
                {
                    HasError = true;
                    curQuery.ErrorMessage.AppendLine("Expression does not exists : " + curToken.StartEndString);
                    ErrorToken = curToken;
                }

                if(methods.Count > 1)
                {
                    HasError = true;
                    curQuery.ErrorMessage.AppendLine("Invalid method definition : " + curToken.StartEndString);
                    ErrorToken = curToken;
                }

                if (methods.Count == 1)
                {
                    var method = methods[0];

                    curQuery = method.Execute(curToken, curQuery);

                    if (curQuery.HasError)
                        return curQuery;

                    if (curQuery.Elements.Count == 0 && EmptyToken == null)
                        EmptyToken = curToken;
                }

                curToken = curToken.Next;

                //skip arg tokens
                while (curToken != null && curToken.IsNextArg)
                {
                    curToken = curToken.Next;
                }

                if (curToken != null && curToken.Kind == LexPath.TokenKind.Undefined && string.IsNullOrEmpty(curToken.PathExp) && curToken.Next == null)
                    curToken = null;
            }

            return curQuery;
        }

        public sQuery Execute(LexPath path, sQuery query)
        {
            var curToken = path.StartToken;

            return Execute(curToken, query);
        }
    }
}
